// Helper functions for volvis.cpp: load, save and edit transfer function files

#include "TFedit.h"
#include <cstdlib>

void clearScreen() {
//	system("clear");
	cout << "\33[2J";
}

// Give user an interface to edit current transfer functions
int editTF(vtkColorTransferFunction *volumeColor, vtkPiecewiseFunction *scalarOpacity,
			vtkPiecewiseFunction *gradientOpacity) {
	while(1) {
		clearScreen();
		int nc, ns, ng, longest;
		nc = volumeColor->GetSize();
		ns = scalarOpacity->GetSize();
		ng = gradientOpacity->GetSize();
		cout.precision(5);
		
		// Print color transfer function in a table
		cout << setw(20) << "Color" << endl;
		cout << setw(4) << "id" << setw(10) << "value" << setw(10) << "red" << setw(10) << "green";
		cout << setw(10) << "blue" << endl;
		for(int i=0; i < nc; i++) {
			double val[6];
			volumeColor->GetNodeValue(i, val);
			cout << setw(4) << i << setw(10) << val[0] << setw(10) << val[1];
			cout << setw(10) << val[2] << setw(10) << val[3] << endl;
		}
		
		// Print opacity transfer functions in a table
		longest = ns;
		if(ng > longest) longest = ng;
		cout << endl << setw(20) << "Scalar opacity" << setw(32) << "Gradient opacity" << endl;
		cout << setw(4) << "id" << setw(10) << "value" << setw(10) << "opacity";
		cout << setw(12) << "id" << setw(10) << "value" << setw(10) << "opacity" << endl;
		for(int i=0; i < longest; i++) {
			double val[4];
			if(i < ns) {
				scalarOpacity->GetNodeValue(i, val);
				cout << setw(4) << i << setw(10) << val[0] << setw(10) << val[1];
			} else {
				cout << setw(24) << " ";
			}
			if(i < ng) {
				gradientOpacity->GetNodeValue(i, val);
				cout << setw(12) << i << setw(10) << val[0] << setw(10) << val[1];
			}
			cout << endl;
		}
		
		
		int action;
		int tf;
		int node;
		cout << "\n1. Edit node\n";
		cout << "2. Add node\n";
		cout << "3. Remove node\n";
		cout << "4. Return\n\n? ";
		cin >> action;
		if(action < 1 || action > 3) {
			break;
		}
		cout << "Transfer function: (1) color, (2) scalar opacity, (3) gradient opacity\n\n? ";
		cin >> tf;
		if(tf < 1 || tf > 3) {
			cout << "Invalid transfer function.";
			continue;
		}
		
		// Get node id if needed
		if(action == 1 || action == 3) {
			cout << "Node ID\n\n? ";
			cin >> node;
		}
		
		if(action == 1 || action == 2) {
			double input[4];
			if(tf == 1) {
				cout << "<value> <r> <g> <b>\n\n? ";
				cin >> input[0] >> input[1] >> input[2] >> input[3];
			} else {
				cout << "<value> <opacity>\n\n? ";
				cin >> input[0] >> input[1];
			}
			
			if(action == 1) { // Edit Node
				if(tf == 1) { // Color
					double val[6];
					volumeColor->GetNodeValue(node, val);
					val[0] = input[0]; val[1] = input[1];
					val[2] = input[2]; val[3] = input[3];
					volumeColor->SetNodeValue(node, val);
				} else if(tf == 2) { // Scalar opacity
					double val[4];
					scalarOpacity->GetNodeValue(node, val);
					val[0] = input[0]; val[1] = input[1];
					scalarOpacity->SetNodeValue(node, val);
				} else { // Gradient opacity
					double val[4];
					gradientOpacity->GetNodeValue(node, val);
					val[0] = input[0]; val[1] = input[1];
					gradientOpacity->SetNodeValue(node, val);
				}
			} else if(action == 2) { // Add Node
				if(tf == 1) // Color
					volumeColor->AddRGBPoint(input[0], input[1], input[2], input[3]);
				else if(tf == 2) // Scalar opacity
					scalarOpacity->AddPoint(input[0], input[1]);
				else // Gradient opacity
					gradientOpacity->AddPoint(input[0], input[1]);
			}
		} else if(action == 3) { // Delete Node
			if(tf == 1) { // Color
				double val[6];
				volumeColor->GetNodeValue(node, val);
				volumeColor->RemovePoint(val[0]);
			} else if(tf == 2) { // Scalar opacity
				double val[4];
				scalarOpacity->GetNodeValue(node, val);
				scalarOpacity->RemovePoint(val[0]);
			} else { // Gradient opacity
				double val[4];
				gradientOpacity->GetNodeValue(node, val);
				gradientOpacity->RemovePoint(val[0]);
			}
		}
	}
	return 0;
}

// Write a file containing given transfer functions
int writeTFFile(const char *filename, vtkColorTransferFunction *volumeColor,
				vtkPiecewiseFunction *scalarOpacity, vtkPiecewiseFunction *gradientOpacity) {
					
	// First check if exists and confirm overwrite
	ifstream inp;
	inp.open(filename);
	inp.close();
	if(!inp.fail()) {
		cout << "File already exists, overwrite? (y/n): ";
		char c;
		cin >> c;
		if(c != 'y') return 1;
	}
	inp.clear(ios::failbit);
	
	ofstream f;
	f.open(filename, fstream::out | fstream::trunc);
	if(f.fail()) {
		cout << "Error opening file to write.\n";
		return 1;
	}
	int nc, ns, ng;
	nc = volumeColor->GetSize();
	ns = scalarOpacity->GetSize();
	ng = gradientOpacity->GetSize();
	
	// Write number of points in each transfer function
	f << nc << " " << ns << " " << ng << endl;
	
	// Write points of color transfer function
	for(int i=0; i<nc; i++) {
		double val[6];
		volumeColor->GetNodeValue(i, val);
		f << val[0] << " " << val[1] << " " << val[2] << " " << val[3]
		  << " " << val[4] << " " << val[5] << endl;
	}
	
	// Write points of scalar opacity transfer function
	for(int i=0; i<ns; i++) {
		double val[4];
		scalarOpacity->GetNodeValue(i, val);
		f << val[0] << " " << val[1] << " " << val[2] << " " << val[3] << endl;
	}
	
	// Write points of gradient opacity transfer function
	for(int i=0; i<ng; i++) {
		double val[4];
		gradientOpacity->GetNodeValue(i, val);
		f << val[0] << " " << val[1] << " " << val[2] << " " << val[3] << endl;
	}
	f.close();
	cout << "Transfer functions writen to file \'" << filename << "\'.\n";
	return 0;
}

// Read given file and put result into given transfer functions
int readTFFile(const char *filename, vtkColorTransferFunction *volumeColor,
				vtkPiecewiseFunction *scalarOpacity, vtkPiecewiseFunction *gradientOpacity) {
	ifstream f;
	f.open(filename);
	if(f.fail()) {
		cout << "Error opening file.\n";
		return 1;
	}
	int nc, ns, ng;
	f >> nc >> ns >> ng;
	
	// Read color transfer function
	volumeColor->RemoveAllPoints();
	for(int i=0; i<nc; i++) {
		double x, r, g, b, midpoint, sharpness;
		f >> x >> r >> g >> b >> midpoint >> sharpness;
		volumeColor->AddRGBPoint(x, r, g, b, midpoint, sharpness);
	}
	
	// Read scalar opacity transfer function
	scalarOpacity->RemoveAllPoints();
	for(int i=0; i<ns; i++) {
		double x, y, midpoint, sharpness;
		f >> x >> y >> midpoint >> sharpness;
		scalarOpacity->AddPoint(x, y, midpoint, sharpness);
	}
	
	// Read gradient opacity transfer function
	gradientOpacity->RemoveAllPoints();
	for(int i=0; i<ng; i++) {
		double x, y, midpoint, sharpness;
		f >> x >> y >> midpoint >> sharpness;
		gradientOpacity->AddPoint(x, y, midpoint, sharpness);
	}
	f.close();
	cout << "Loaded transfer functions from \'" << filename << "\'.\n";
	return 0;
}
